/workspace/Dropbox/projects/scicloj/noj/notebooks/intro/visualization.clj
(ns intro.visualization
  (:require [scicloj.kind-clerk.api :as kind-clerk]
            [tablecloth.api :as tc]
            [aerial.hanami.common :as hc]
            [aerial.hanami.templates :as ht]
            [scicloj.noj.v1.vis.hanami.templates :as vht]
            [scicloj.noj.v1.vis :as vis]
            [scicloj.noj.v1.stats :as stats]
            [tech.v3.datatype :as dtype]
            [tech.v3.datatype.functional :as fun]
            [scicloj.kindly.v3.api :as kindly]
            [scicloj.kindly.v3.kind :as kind]
            [hiccup.core :as hiccup]
            [clojure2d.color :as color]))
...

Raw html

(-> "

Hello, Noj.

" vis/raw-html)
...
(-> [:svg {:height 210
           :width 500}
     [:line {:x1 0
             :y1 0
             :x2 200
             :y2 200
             :style "stroke:rgb(255,0,0);stroke-width:2"}]]
    hiccup/html
    vis/raw-html)
...

Visualizing datases with Hanami

Noj offers a few convenience functions to make Hanami plotting work smoothly with Tablecloth and Kindly.

(def mtcars
  (-> "data/mtcars.csv"
      (tc/dataset {:key-fn keyword})))
...
(def iris
  (-> "data/iris.csv"
      (tc/dataset {:key-fn keyword})))
...
(def random-walk
  (let [n 20]
    (-> {:x (range n)
         :y (->> (repeatedly n #(- (rand) 0.5))
                 (reductions +))}
        tc/dataset)))
...

A simple plot

We can plot a Tablecloth datasete using a Hanami template:

(-> random-walk
    (vis/hanami-plot ht/point-chart
                     {:MSIZE 200}))
...

Let us look inside the resulting vega-lite space. We can see the dataset is included as CSV:

(-> random-walk
    (vis/hanami-plot ht/point-chart
                     {:MSIZE 200})
    kind/pprint)
{:encoding
 {:y {:field "y", :type "quantitative"},
  :x {:field "x", :type "quantitative"}},
 :mark {:type "circle", :size 200, :tooltip true},
 :width 400,
 :background "floralwhite",
 :height 300,
 :data
 {:values
  "x,y\n0,0.46278344327931786\n1,0.7225300428080409\n2,0.6825960991459601\n3,1.1049084925640287\n4,1.5905499030440238\n5,1.1175203576814434\n6,1.1464975887685016\n7,1.0836373417840375\n8,1.0564477984442235\n9,1.5542711767827888\n10,1.1411397794537894\n11,0.8097630170779119\n12,0.6711656092860713\n13,0.2685734593897352\n14,0.5441913226772874\n15,0.08645249109930675\n16,0.41432882437060037\n17,0.17021470305698538\n18,-0.22806651697229863\n19,-0.05236195858138093\n",
  :format {:type "csv"}}}

Additional Hanami templates

The scicloj.noj.v1.vis.hanami.templates namespace add Hanami templates to Hanami's own collection.

(-> mtcars
    (vis/hanami-plot vht/boxplot-chart
                     {:X :gear
                      :XTYPE :nominal
                      :Y :mpg}))
...

Layers

(-> random-walk
    (vis/hanami-layers
     {:TITLE "points and a line"}
     [(vis/hanami-plot nil
                       ht/point-chart
                       {:MSIZE 400})
      (vis/hanami-plot nil
                       ht/line-chart
                       {:MSIZE 4
                        :MCOLOR "brown"})]))
...

Concatenation

(-> random-walk
    (vis/hanami-vconcat
     {}
     [(vis/hanami-plot nil
                       ht/point-chart
                       {:MSIZE 400
                        :HEIGHT 100
                        :WIDTH 100})
      (vis/hanami-plot nil
                       ht/line-chart
                       {:MSIZE 4
                        :MCOLOR "brown"
                        :HEIGHT 100
                        :WIDTH 100})]))
...
(-> random-walk
    (vis/hanami-hconcat
     {}
     [(vis/hanami-plot nil
                       ht/point-chart
                       {:MSIZE 400
                        :HEIGHT 100
                        :WIDTH 100})
      (vis/hanami-plot nil
                       ht/line-chart
                       {:MSIZE 4
                        :MCOLOR "brown"
                        :HEIGHT 100
                        :WIDTH 100})]))
...

Linear regression

(-> mtcars
    (stats/add-predictions :mpg [:wt]
                           {:model-type :smile.regression/ordinary-least-square})
    (vis/hanami-layers {}
                       [(vis/hanami-plot nil
                                         ht/point-chart
                                         {:X :wt
                                          :Y :mpg
                                          :MSIZE 200
                                          :HEIGHT 200
                                          :WIDTH 200})
                        (vis/hanami-plot nil
                                         ht/line-chart
                                         {:X :wt
                                          :Y :mpg-prediction
                                          :MSIZE 5
                                          :MCOLOR "purple"
                                          :YTITLE :mpg})]))
...

Histogram

(-> iris
    (vis/hanami-histogram :sepal-width
                          {:nbins 10}))
...

Combining a few things together

The following is inspired by the example at Plotnine's main page. Note how we add regression lines here.

(let [pallete (->> :accent
                   color/palette
                   (mapv color/format-hex))]
  (-> mtcars
      (tc/group-by :gear {:result-type :as-map})
      (->> (sort-by key)
           (map-indexed
            (fn [i [group-name ds]]
              (-> ds
                  (stats/add-predictions :mpg [:wt]
                                         {:model-type :smile.regression/ordinary-least-square})
                  (vis/hanami-layers {:TITLE (str "grear=" group-name)}
                                     [(vis/hanami-plot nil
                                                       ht/point-chart
                                                       {:X :wt
                                                        :Y :mpg
                                                        :MSIZE 200
                                                        :MCOLOR (pallete i)
                                                        :HEIGHT 200
                                                        :WIDTH 200})
                                      (vis/hanami-plot nil
                                                       ht/line-chart
                                                       {:X :wt
                                                        :Y :mpg-prediction
                                                        :MSIZE 5
                                                        :MCOLOR (pallete i)
                                                        :YTITLE :mpg})]
                                     ))))
           (vis/hanami-hconcat nil {}))))
...
(let [pallete (->> :accent
                   color/palette
                   (mapv color/format-hex))]
  (-> iris
      (tc/group-by :species {:result-type :as-map})
      (->> (sort-by key)
           (map-indexed
            (fn [i [group-name ds]]
              (-> ds
                  (vis/hanami-histogram :sepal-width
                                        {:nbins 10}))))
           (vis/hanami-vconcat nil {}))))
...
:bye
:bye